home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / src / exampleCode / opengl / xlib / pup.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-08-02  |  40.4 KB  |  1,562 lines

  1. /*
  2.  * Copyright 1994, Silicon Graphics, Inc.
  3.  * All Rights Reserved.
  4.  *
  5.  * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
  6.  * the contents of this file may not be disclosed to third parties, copied or
  7.  * duplicated in any form, in whole or in part, without the prior written
  8.  * permission of Silicon Graphics, Inc.
  9.  *
  10.  * RESTRICTED RIGHTS LEGEND:
  11.  * Use, duplication or disclosure by the Government is subject to restrictions
  12.  * as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
  13.  * and Computer Software clause at DFARS 252.227-7013, and/or in similar or
  14.  * successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
  15.  * rights reserved under the Copyright Laws of the United States.
  16.  */
  17. /*
  18.  * menu code with a sordid history.
  19.  */
  20.  
  21. #include    <stdlib.h>
  22. #include    <string.h>
  23. #include    <stdarg.h>
  24. #include    <ctype.h>
  25. #include    <assert.h>
  26. #include     <unistd.h>
  27. #include     <bstring.h>
  28. #include    <sys/time.h>
  29. #include    <sys/times.h>
  30. #include    <sys/param.h>
  31. #include     <sys/types.h>
  32.  
  33.  
  34. #include    <stdio.h>
  35.  
  36. #include    <X11/Xlib.h>    /* XXX do we need all this shit */
  37. #include    <X11/Xutil.h>
  38. #include    <X11/Xmd.h>
  39. #include    <X11/cursorfont.h>
  40.  
  41. #include    "pup.h"
  42.  
  43.  
  44. #define POST_TIMEOUT    160
  45. #define HOG_TIMEOUT    1000
  46. #define MOTION_HISTORY    4
  47.  
  48. static struct hbuf { int x, y; } history[MOTION_HISTORY];
  49. static int mhead;
  50. static int PostTimeout;
  51. static int HogTimeout;
  52. static int MenuState;
  53. static unsigned int StateTimeout;
  54.  
  55. static void set_state(int);
  56. static void post_submenu(void);
  57. static void next_event(Display*,XEvent*);
  58. static void timeout_happened(void);
  59. static int history_allows(void);
  60.  
  61. #define MS_NORMALSTATE        0
  62. #define    MS_PULLRIGHTPENDING    1
  63. #define MS_PULLRIGHTACTIVE    2
  64. #define MS_HOGSTATE        3
  65.  
  66. static char* defaultMenuFont = "-*-helvetica-bold-o-normal--14-100-*";
  67.  
  68. #define ALLOC(x)  malloc(x)
  69. #define FREE(x)   free(x)
  70. #define STRDUP(x) strdup(x)
  71.  
  72. static char m_class[] = "Menu";
  73. static char m_inst[] = "menu";
  74.  
  75. static void render_outline (Display* , Window , GC *, int , int , int , int );
  76. static void render_fill (Display* , Window , GC *, GC, int , int , int , int );
  77.  
  78. typedef struct mFont {
  79.     XFontStruct*    finfo;
  80.     char*        fontName;
  81.     Display        *dpy;
  82.     struct mFont*    next;
  83. } mFont;
  84.  
  85. #define Menu_itemTopInset 2
  86. #define Menu_itemBottomInset 2
  87. #define Menu_itemLeftInset 4
  88. #define Menu_itemRightInset 4
  89. #define Menu_itemOffset 2
  90.  
  91. #define Menu_topInset 4
  92. #define Menu_bottomInset 4
  93. #define Menu_leftInset 4
  94. #define Menu_rightInset 4
  95.  
  96. #define Menu_arrowLeftInset 7
  97. #define Menu_arrowWidth 8
  98. #define Menu_arrowHeight 8
  99. #define Menu_arrowRightInset 0
  100.  
  101. #define  Menu_checkLeftInset 4
  102. #define  Menu_checkWidth 11
  103. #define  Menu_checkHeight 11
  104. #define  Menu_checkRightInset 3
  105.  
  106. #define Menu_subMenuXOffset -2
  107. #define Menu_subMenuYOffset -3
  108.  
  109. #define Menu_shadowXOffset 5 + 2    /* +2 for border width */
  110. #define Menu_shadowYOffset 5 + 2
  111.  
  112. #define Menu_titleYGap 6
  113.  
  114. #define Menu_arrowAdjust    \
  115.     (Menu_arrowLeftInset+Menu_arrowWidth+Menu_arrowRightInset)
  116.  
  117. #define Menu_checkAdjust \
  118.     (Menu_checkLeftInset + Menu_checkWidth + Menu_checkRightInset)
  119.  
  120. #define Menu_widthAdjust (Menu_leftInset+Menu_rightInset)
  121.  
  122. #define Menu_heightAdjust (Menu_topInset+Menu_bottomInset)
  123.  
  124. /*---------------------------------------------------------------------- */
  125.  
  126. typedef struct struct_PopupInfo {
  127.     struct struct_PopupInfo    *next;
  128.     Display*        dpy;
  129.     int            screen;
  130.     int            overFirst;
  131.     GC            black,
  132.             darkGrey,
  133.             grey,
  134.             lightGrey,
  135.             lightWhiteGrey,
  136.             white;
  137.     int            whitePixel,
  138.             blackPixel,
  139.             lightGreyPixel;
  140.     Colormap        colormap;
  141.     Visual*        visual;
  142.     int            depth;
  143.     Pixmap        shadowPixmap;
  144.  
  145.     Pixmap        lightWhiteGreyPixmap;
  146.     GC            titleColors[4];
  147.     GC            regularColors[4];
  148.     GC            selectedColors[4];
  149.     GC            checkBoxOnColors[4];
  150.     GC            checkBoxOffColors[4];
  151.     Cursor        arrowCursor;
  152. } PopupInfo;
  153.  
  154. typedef struct Menu {
  155.     PopupInfo*            popupInfo;
  156.     struct MenuItem*    head;
  157.     struct MenuItem*    tail;
  158.     unsigned char    popping;
  159.     unsigned char    isTitle;
  160.     unsigned char    mapped;
  161.     unsigned char    button;
  162.     unsigned char    layoutDone;
  163.     unsigned char    overFirst;
  164.     short        width, height;
  165.     Window    contentWindow;
  166.     Window    shadowWindow;
  167.     int        initialX;
  168.     struct Menu*    menuTitle;
  169.  
  170.     struct MenuItem*    currentItem;    /* current selected item in this menu */
  171.     struct Menu*    currentSubMenu;    /* current active sub menu (if any) */
  172.     struct Menu*    stackPrev;    /* previous menu in stack from this menu */
  173.  
  174.     struct mFont* menuFont;
  175.     int        (*menuFunc)(int);
  176. } Menu;
  177. static void draw_check(PopupInfo* , Window , int , int , int , int );
  178. static PopupInfo* create_PopupInfo(Display* , int );
  179. static Menu* create_menu(Display* , int , int);
  180. static void set_title_menu(Menu* , char *);
  181. static void destroy_menu(Menu* );
  182. static int popup_menu(Menu* , int, int , int );
  183. static struct MenuItem* find_item(Menu*,int);
  184.  
  185. #define MenuItem_normal 0
  186. #define MenuItem_hasCheckBox 1
  187.  
  188. /* MenuItem::visualState */
  189. #define MenuItem_disabled 0
  190. #define MenuItem_quiet 1
  191. #define MenuItem_selected 2
  192.  
  193. /* Generic menu item type.  Subclasses provide a particular thing to */
  194. /* image as the label part of the menu item. */
  195.  
  196. typedef struct MenuItem {
  197.     struct Menu*    parent;
  198.     struct MenuItem*    nextItem;
  199.     struct Menu*    subMenu;
  200.     char    type;
  201.     char    visualState;
  202.     int        checkState;
  203.     int        enabled;
  204.     short    index;            /* item # for client */
  205.     int        pickValue;
  206.     int        inset;
  207.     short    x, y, width, height;    /* position in parent */
  208.     short    trueWidth, trueHeight;
  209.  
  210.     char*    label;            /* If it is a label, these are */
  211.     int        len;            /* used.  If label is zero, it */
  212.     struct mFont*    font;            /* is assumed to be a line */
  213.  
  214.     int useMenuFunc;
  215.     int    (*itemFunc)(int);
  216. } MenuItem;
  217. static MenuItem* create_item(struct Menu* , int , char* );
  218. static void reset_MenuItem(MenuItem*);
  219. static void resize_item(MenuItem*, int, int);
  220. static void move_item(MenuItem*, int, int);
  221. static void destroy_item(MenuItem* );
  222. static void render_background_item(MenuItem* );
  223. static void enter_item(MenuItem *);
  224. static void leave_item(MenuItem *);
  225. static void render_item(MenuItem *);
  226. static void set_enable_item(MenuItem* , int );
  227. static void set_check_item(MenuItem* , int );
  228.  
  229. /*---------------------------------------------------------------------*/
  230.  
  231. typedef struct aFuncElement {
  232.     int            (*func)();
  233.     struct aFuncElement *next;
  234. } aFuncElement;
  235.  
  236. typedef struct {
  237.     aFuncElement    *head;
  238.     aFuncElement    *tail;
  239. } aFuncChain;
  240.  
  241. static aFuncChain *
  242. create_aFuncChain(void) {
  243.     aFuncChain    *ret;
  244.     ret = ALLOC(sizeof *ret);
  245.     bzero(ret, sizeof *ret);
  246.     return ret;
  247. }
  248.  
  249. static void
  250. extend_aFuncChain(aFuncChain* this, int (*newfunc)()) {
  251.     aFuncElement    *newElement;
  252.     if (newfunc) {
  253.     newElement = ALLOC(sizeof *newElement);
  254.     newElement->next = 0;
  255.     newElement->func = newfunc;
  256.     if (this->head == 0)
  257.         this->head = this->tail = newElement;
  258.     else {
  259.         this->tail->next = newElement;
  260.         this->tail = newElement;
  261.     }
  262.     }
  263. }
  264.  
  265. static int
  266. call_aFuncChain(aFuncChain* this, int ret) {
  267.     aFuncElement    *who;
  268.  
  269.     for (who = this->head; who; who = who->next)
  270.     ret = (who->func)(ret);
  271.     return ret;
  272. }
  273.  
  274. static void
  275. destroy_aFuncChain(aFuncChain* this) {
  276.     aFuncElement    *who, *youwho;
  277.     for (who = this->head; who; who = youwho) {
  278.     youwho = who->next;
  279.     FREE(who);
  280.     }
  281.     FREE(this);
  282. }
  283.     
  284. /*---------------------------------------------------------------------*/
  285.  
  286. mFont* allFontInfo;
  287. static mFont*
  288. create_font(Display* dpy, const char* fname) {
  289.     mFont*    this = allFontInfo;
  290.     XFontStruct    *font = 0;
  291.     
  292.     for ( ; this ; this = this->next)
  293.     if (this->dpy == dpy) {
  294.         if (fname == 0 && this->fontName == 0)
  295.         return this;
  296.         if (fname == 0 || this->fontName == 0)
  297.         continue;
  298.         if (strcmp(fname, this->fontName) == 0)
  299.         return this;
  300.     }
  301.     if (fname)
  302.     font = XLoadQueryFont(dpy, fname);
  303.     if (font == 0) {
  304.     font = XLoadQueryFont(dpy, defaultMenuFont);
  305.     if (font == 0)
  306.         font = XLoadQueryFont(dpy, "fixed");
  307.     }
  308.     this = ALLOC(sizeof *this);
  309.     this->dpy = dpy;
  310.     this->next = allFontInfo;
  311.     allFontInfo = this;
  312.     this->finfo = font;
  313.     this->fontName = fname ? STRDUP(fname) : 0;
  314.     return this;
  315. }
  316.  
  317. static int
  318. strwidth_font(mFont* this, char* str, int len) {
  319.     int direction, ascent, descent;
  320.     XCharStruct    size;
  321.  
  322.     XTextExtents(this->finfo, str, len, &direction, &ascent, &descent, &size);
  323.     return size.lbearing + size.rbearing;
  324. }
  325.  
  326. typedef int (*PFI)();
  327. static void
  328. do_addpup(Menu* pup,char *str,va_list args) {
  329.     MenuItem* it;
  330.     int lastItemNo = 0;
  331.     char *cp;
  332.  
  333.     for (it = pup->head; it; it = it->nextItem)
  334.      if (it->index)
  335.         lastItemNo = it->index;
  336.     for (cp = str;;cp++) {
  337.     /*
  338.     ** First collect all the characters of the string, setting
  339.     ** pieces of flag information as we go ...
  340.     */
  341.     PFI    entryFunc;
  342.     int    ignoreMenuFunc;
  343.     int    isTitle;
  344.     int    hasUnderline;
  345.     int    returnValue;
  346.     Menu*    subMenu;
  347.     char    collectName[512], *dp;
  348.  
  349.     entryFunc = 0;
  350.     ignoreMenuFunc = isTitle = hasUnderline = 0;
  351.     returnValue = lastItemNo+1;
  352.     subMenu = 0;
  353.     dp = collectName;
  354.  
  355.     while (*cp && *cp != '|') {
  356.         if (*cp != '%')
  357.         *dp++ = *cp++;
  358.         else {
  359.         switch (*++cp) {
  360.             case 't': isTitle = True; break;
  361.             case 'l': hasUnderline = True; break;
  362.             case 'n': ignoreMenuFunc = True; /* Fall through to next case */
  363.             case 'f': entryFunc = va_arg(args, PFI); break;
  364.             case 'F': pup->menuFunc = va_arg(args, PFI); break;
  365.             case 'm': subMenu = va_arg(args, Menu*); break;
  366.             case 'x':
  367.               returnValue = strtol(++cp, &cp, 10);
  368.               cp--;
  369.               break;
  370.             case '%': *dp++ = '%'; break;
  371.         }
  372.         cp++;
  373.         }
  374.     }
  375.  
  376.     if (dp == collectName)
  377.         break;
  378.     *dp = 0;
  379.     /*
  380.     ** Now we've collected all the info, add the item(s) to the menu
  381.     */
  382.     if (isTitle)
  383.         set_title_menu((Menu*) pup, collectName);
  384.     else {
  385.         it = create_item((Menu*) pup, ++lastItemNo, collectName);
  386.         it->pickValue = returnValue;
  387.         if (subMenu)
  388.         it->subMenu = subMenu;
  389.         if (ignoreMenuFunc)
  390.         it->useMenuFunc = 0;
  391.         it->itemFunc = entryFunc;
  392.         if (hasUnderline)
  393.         create_item((Menu*) pup, 0, 0);
  394.     }
  395.     if (*cp == 0)
  396.         break;
  397.     }
  398. }
  399.  
  400. long
  401. newpup(Display *dpy, int screen) {
  402.     return (long) create_menu(dpy, screen, False);
  403. }
  404.  
  405. void 
  406. addtopup(long pup, char *str, ...) {
  407.     va_list args;
  408.     va_start(args, str);
  409.     (void) do_addpup((Menu*) pup, str, args);
  410.     va_end(args);
  411. }
  412.  
  413. long
  414. defpup(Display *dpy, int scrn, char *str, ...) {
  415.     va_list args;
  416.     long ret = newpup(dpy, scrn);
  417.     va_start(args, str);
  418.     (void) do_addpup((Menu*) ret, str, args);
  419.     va_end(args);
  420.     return ret;
  421. }
  422.  
  423. long
  424. dopup(long menu) {
  425.     int ret;
  426.     Menu* pup = (Menu*) menu;
  427.     Window junkw;
  428.     int junki, x, y;
  429.     XQueryPointer(pup->popupInfo->dpy,
  430.     RootWindow(pup->popupInfo->dpy, pup->popupInfo->screen),
  431.     &junkw, &junkw, &x, &y, &junki, &junki, (unsigned *)&junki);
  432.     ret = popup_menu((Menu*) menu, 3, x, y);
  433.     XFlush(((Menu*)menu)->popupInfo->dpy);
  434.     return ret;
  435. }
  436.  
  437. void
  438. freepup(long menu) {
  439.     if (menu)
  440.     destroy_menu((Menu*) menu);
  441. }
  442.  
  443. void
  444. setpup(long menu, long itemNo, unsigned long arg) {
  445.     MenuItem* it;
  446.  
  447.     if (it = find_item((Menu*) menu, itemNo)) {
  448.     set_enable_item(it, ((arg & PUP_GREY) == 0));
  449.     set_check_item(it, (arg & (PUP_CHECK|PUP_BOX)));
  450.     }
  451. }
  452.  
  453. static void realize_menu(Menu* , int , int );
  454. static void map_menu(Menu* );
  455. static void enter_menu(Window , int , int );
  456. static void leave_item_menu(Menu* );
  457. static void move_to(int , int );
  458. static void enter_item_menu(Menu*,MenuItem* );
  459. static void cancel_menu(Menu* );
  460. static void render_menu(Menu* , int , int );
  461. static void append_item_menu(Menu* , MenuItem* );
  462. static void layout_menu(Menu* );
  463. static Menu* find_menu(Menu* , Window );
  464. static void resize_menu(Menu*, int, int);
  465.  
  466. static Menu* currentMenu;
  467.  
  468. static char shadowBits[16*2] = {
  469.     0xAA, 0xAA, 0x55, 0x55,
  470.     0xAA, 0xAA, 0x55, 0x55,
  471.     0xAA, 0xAA, 0x55, 0x55,
  472.     0xAA, 0xAA, 0x55, 0x55,
  473.     0xAA, 0xAA, 0x55, 0x55,
  474.     0xAA, 0xAA, 0x55, 0x55,
  475.     0xAA, 0xAA, 0x55, 0x55,
  476.     0xAA, 0xAA, 0x55, 0x55,
  477. };
  478. #define checkWidth 16
  479. #define checkHeight 8
  480. static char checkBits[] = {
  481.    0x00, 0x0f,
  482.    0x86, 0x03,
  483.    0xcf, 0x01,
  484.    0xec, 0x00,
  485.    0x78, 0x00,
  486.    0x38, 0x00,
  487.    0x10, 0x00,
  488.    0x10, 0x00,
  489. };
  490.  
  491. static char const *
  492. getresource(const char* class, const char* instance, ...) {
  493.     return 0;
  494. }
  495.  
  496. static void
  497. grab_everything(XEvent* ev, PopupInfo* pInfo) {
  498.     int press, code;
  499.     static have_active_grab;
  500.  
  501.     switch (ev->type) {
  502.       case KeyPress:
  503.     press = 1;
  504.     code = ev->xkey.keycode;
  505.     break;
  506.       case KeyRelease:
  507.     press = 0;
  508.     code = ev->xkey.keycode;
  509.     break;
  510.       case ButtonPress:
  511.     press = 1;
  512.     code = ev->xbutton.button;
  513.     break;
  514.       case ButtonRelease:
  515.     press = 0;
  516.     code = ev->xbutton.button;
  517.     break;
  518.       default:
  519.     return;
  520.     }
  521.  
  522.     if (press) {
  523.     if (!have_active_grab) {
  524.         Cursor c = pInfo->arrowCursor;
  525.         Display *dpy = pInfo->dpy;
  526.         int gp, gk;
  527.         Window window = RootWindow(dpy, pInfo->screen);
  528. #define POINTER_GRAB_MASK (PointerMotionMask|ButtonPressMask|ButtonReleaseMask)
  529.  
  530.         gp = XGrabPointer(dpy, window, True, POINTER_GRAB_MASK,
  531.                    GrabModeAsync, GrabModeAsync,
  532.                    None, c, CurrentTime);
  533.         /*
  534.         gk = XGrabKeyboard(dpy, evw->frame, True, GrabModeAsync,
  535.                     GrabModeAsync, CurrentTime);
  536.         */
  537.         if (/*(gk == GrabSuccess) &&*/ (gp == GrabSuccess)) {
  538.         /* winner */
  539.         have_active_grab = 1;
  540.         } else {
  541.         /* loser! no grab now, maybe later */
  542.         if (gp == GrabSuccess) {
  543.             XUngrabPointer(dpy, CurrentTime);
  544.         }
  545.         /*
  546.         if (gk == GrabSuccess) {
  547.             XUngrabKeyboard(dpy, CurrentTime);
  548.         }
  549.         */
  550.         }
  551.     }
  552.     } else {
  553.     Display *dpy = pInfo->dpy;
  554.     if (have_active_grab) {
  555.         XUngrabPointer(dpy, CurrentTime);
  556.         XUngrabKeyboard(dpy, CurrentTime);
  557.         have_active_grab = 0;
  558.     }
  559.     }
  560. }
  561.  
  562. static Visual *
  563. find_popup(Display *dpy, int screen, int *depth) {
  564.  
  565.     XVisualInfo *vinfo, template;
  566.     int nvisuals;
  567.  
  568.     template.screen = screen;
  569.     vinfo = XGetVisualInfo(dpy, VisualScreenMask, &template, &nvisuals);
  570.  
  571.     /* SGI convention is that first visual is PUP visual XXX */
  572.     *depth = vinfo->depth;
  573.     return vinfo->visual;
  574. }
  575.  
  576.  
  577. /*---------------------------------------------------------------------- */
  578. /* "allPopupInfo" points to a list of structures, one per screen, which  */
  579. /* contains information about how popup menus are rendered on that     */
  580. /* screen.  In the future, it may be per screen/visual pair, so that     */
  581. /* not all popups have to be in the same planes.             */
  582. /*---------------------------------------------------------------------- */
  583.  
  584. static PopupInfo* allPopupInfo;
  585.  
  586. static PopupInfo*
  587. create_PopupInfo(Display* dpy, int screen) {
  588.     PopupInfo* ret = allPopupInfo;
  589.     XGCValues    gcv;
  590.     int        isOverlay = 1;
  591.     XColor    light_def, medium_def, dark_def;
  592.     Window    root = RootWindow(dpy, screen);
  593.     const char*    cname;
  594.  
  595.     while (ret) {
  596.     if (ret->dpy == dpy && ret->screen == screen)
  597.         return ret;
  598.     ret = ret->next;
  599.     }
  600.     ret = ALLOC(sizeof(*ret));
  601.     ret->dpy = dpy;
  602.     ret->screen = screen;
  603.     ret->visual = find_popup(dpy, screen, &ret->depth);
  604.     ret->colormap = XCreateColormap(dpy, root, ret->visual, AllocNone);
  605.     ret->arrowCursor = XCreateFontCursor(dpy, XC_arrow);
  606.     ret->overFirst = False;
  607.     if (cname = getresource(m_class, m_inst, "Timeout", "hogTimeout", 0)) {
  608.     HogTimeout = atoi(cname);
  609.     FREE((char *)cname);
  610.     } else
  611.     HogTimeout = HOG_TIMEOUT;
  612.     if (cname = getresource(m_class, m_inst, "Timeout", "postTimeout", 0)) {
  613.     PostTimeout = atoi(cname);
  614.     FREE((char *)cname);
  615.     } else
  616.     PostTimeout = POST_TIMEOUT;
  617.     if (cname = getresource(m_class, m_inst, "OverFirst", "overFirst", 0)) {
  618.     if (strcasecmp(cname, "true") == 0)
  619.         ret->overFirst = True;
  620.     FREE((char *)cname);
  621.     }
  622.     
  623.     /* First color in the menu colormap is medium (default grey) */
  624.     if (cname = getresource(m_class, m_inst, "Background", "mediumColor", 0)) {
  625.     XParseColor(dpy, ret->colormap, cname, &medium_def);
  626.     FREE((char *)cname);
  627.     } else
  628.     medium_def.red = medium_def.green = medium_def.blue = 0xaa00;
  629.     XAllocColor(dpy, ret->colormap, &medium_def);
  630.  
  631.     /* 2nd color in the menu colormap is dark (default black) */
  632.     if (cname = getresource(m_class, m_inst, "Foreground", "darkColor", 0)) {
  633.     XParseColor(dpy, ret->colormap, cname, &dark_def);
  634.     FREE((char *)cname);
  635.     } else
  636.     dark_def.red = dark_def.green = dark_def.blue = 0;
  637.     XAllocColor(dpy, ret->colormap, &dark_def);
  638.  
  639.     /* 3nd color in the menu colormap is light (default white) */
  640.     if (cname = getresource(m_class, m_inst, "Background", "lightColor", 0)) {
  641.     XParseColor(dpy, ret->colormap, cname, &light_def);
  642.     FREE((char *)cname);
  643.     } else
  644.     light_def.red = light_def.green = light_def.blue = 0xff00;
  645.     XAllocColor(dpy, ret->colormap, &light_def);
  646.  
  647.     ret->shadowPixmap =
  648.         XCreatePixmapFromBitmapData(dpy, root, shadowBits, 16, 16,
  649.                     isOverlay ? 0 : light_def.pixel,
  650.                     dark_def.pixel,
  651.                     ret->depth);
  652.     
  653.     gcv.foreground = dark_def.pixel;
  654.     gcv.stipple = XCreatePixmapFromBitmapData(dpy, root, checkBits,
  655.                          checkWidth, checkHeight, 1, 0, 1);
  656.     ret->black = XCreateGC(dpy, ret->shadowPixmap, 
  657.                GCForeground|GCStipple, &gcv);
  658.     XFreePixmap(dpy, gcv.stipple);
  659.     gcv.fill_style = FillOpaqueStippled;
  660.     gcv.stipple = XCreatePixmapFromBitmapData(dpy, root,
  661.                           shadowBits, 16, 16, 0, 1, 1);
  662.     gcv.foreground = medium_def.pixel;
  663.     ret->lightGrey = XCreateGC(dpy, ret->shadowPixmap, GCForeground, &gcv);
  664.     gcv.background = dark_def.pixel;
  665.     ret->darkGrey = XCreateGC(dpy, ret->shadowPixmap,
  666.             GCForeground|GCBackground|GCFillStyle|GCStipple, &gcv);
  667.     gcv.foreground = light_def.pixel;
  668.     ret->grey = XCreateGC(dpy, ret->shadowPixmap,
  669.             GCForeground|GCBackground|GCFillStyle|GCStipple, &gcv);
  670.     ret->white = XCreateGC(dpy, ret->shadowPixmap, GCForeground, &gcv);
  671.     gcv.background = medium_def.pixel;
  672.     ret->lightWhiteGrey = XCreateGC(dpy, ret->shadowPixmap,
  673.             GCForeground|GCBackground|GCFillStyle|GCStipple, &gcv);
  674.  
  675.     ret->whitePixel = light_def.pixel;
  676.     ret->blackPixel = dark_def.pixel;
  677.     ret->lightGreyPixel = medium_def.pixel;
  678.  
  679.     XFreePixmap(dpy, gcv.stipple);
  680. #ifdef DEBUG_RENDER
  681.     {    /* Pick the four wild colors to see how things get framed! */
  682.     int i;
  683.     for (i = 0; i < 4; i++) {
  684.         gcv.foreground = i+1;
  685.         ret->regularColors[i]=
  686.         XCreateGC(dpy, ret->shadowPixmap, GCForeground, &gcv);
  687.     }
  688.     }
  689. #else
  690.     ret->regularColors[0] = ret->white;
  691.     ret->regularColors[1] = ret->darkGrey;
  692.     ret->regularColors[2] = ret->white;
  693.     ret->regularColors[3] = ret->grey;
  694. #endif
  695.     ret->titleColors[0] = ret->white;
  696.     ret->titleColors[1] = ret->darkGrey;
  697.     ret->titleColors[2] = ret->lightWhiteGrey;
  698.     ret->titleColors[3] = ret->grey;
  699.     ret->selectedColors[0] = ret->white;
  700.     ret->selectedColors[1] = ret->darkGrey;
  701.     ret->selectedColors[2] = ret->white;
  702.     ret->selectedColors[3] = ret->white;
  703.     ret->checkBoxOnColors[0] = ret->black;
  704.     ret->checkBoxOnColors[1] = ret->white;
  705.     ret->checkBoxOnColors[2] = ret->lightGrey;
  706.     ret->checkBoxOnColors[3] = ret->white;
  707.     ret->checkBoxOffColors[0] = ret->darkGrey;
  708.     ret->checkBoxOffColors[1] = ret->darkGrey;
  709.     ret->checkBoxOffColors[2] = ret->white;
  710.     ret->checkBoxOffColors[3] = ret->darkGrey;
  711.  
  712.     ret->lightWhiteGreyPixmap =
  713.         XCreatePixmapFromBitmapData(dpy, root, shadowBits, 16, 16,
  714.                          medium_def.pixel,
  715.                          light_def.pixel,
  716.                          ret->depth);
  717.  
  718.     ret->next = allPopupInfo;
  719.     allPopupInfo = ret;
  720.     return ret;
  721. }
  722.  
  723. /*---------------------------------------------------------------------- */
  724.  
  725. static Menu*
  726. create_menu(Display* dpy, int screen, int isTitle) {
  727.     Menu*            ret;
  728.     XSetWindowAttributes    init;
  729.     long            mask;
  730.  
  731.     ret = (Menu*) ALLOC(sizeof *ret);
  732.     bzero(ret, sizeof *ret);
  733.  
  734.     ret->popupInfo = create_PopupInfo(dpy, screen);
  735.     if (ret->isTitle = isTitle) {
  736.     ret->menuFont = create_font(dpy,
  737.         getresource("MenuTitle", "menuTitle", "Font", "font", 0));
  738.     init.background_pixel = ret->popupInfo->lightGreyPixel;
  739.     mask = CWBackPixel;
  740.     } else {
  741.     ret->menuFont = create_font(dpy,
  742.                 getresource(m_class, m_inst, "Font", "font", 0));
  743.     init.background_pixmap = ret->popupInfo->lightWhiteGreyPixmap;
  744.     mask = CWBackPixmap;
  745.     }
  746.     init.border_pixel = ret->popupInfo->blackPixel;
  747.     init.override_redirect = True;
  748.     init.cursor = ret->popupInfo->arrowCursor;
  749.     init.colormap = ret->popupInfo->colormap;
  750.     init.event_mask = EnterWindowMask|LeaveWindowMask|ButtonPressMask
  751.             |ButtonReleaseMask|ExposureMask|PointerMotionMask
  752.             |ButtonMotionMask;
  753.  
  754.     ret->contentWindow = XCreateWindow(dpy, RootWindow(dpy, screen),
  755.     0, 0,
  756.     100, 100,
  757.     1,
  758.     ret->popupInfo->depth,
  759.     InputOutput,
  760.     ret->popupInfo->visual,
  761.     mask|CWColormap|CWEventMask|CWBorderPixel
  762.         |CWOverrideRedirect|CWCursor,
  763.     &init
  764.     );
  765.  
  766.     init.background_pixmap = ret->popupInfo->shadowPixmap;
  767.     init.event_mask = ButtonPressMask | ButtonReleaseMask;
  768.     ret->shadowWindow = XCreateWindow(dpy, RootWindow(dpy, screen),
  769.         0, 0,
  770.         100, 100,
  771.         0, ret->popupInfo->depth,
  772.         InputOutput, ret->popupInfo->visual,
  773.         CWEventMask|CWColormap|CWBackPixmap
  774.         |CWBorderPixel|CWOverrideRedirect|CWCursor,
  775.         &init
  776.     );
  777.     return ret;
  778. }
  779.  
  780. static void
  781. set_title_menu(Menu* this, char *title) {
  782.     if (this->isTitle)
  783.     return;
  784.     if (this->menuTitle)
  785.     destroy_menu(this->menuTitle);
  786.     this->menuTitle = create_menu(this->popupInfo->dpy,
  787.                     this->popupInfo->screen, True);
  788.     create_item(this->menuTitle, 0, title);
  789.     this->layoutDone = 0;
  790. }
  791.  
  792. static void
  793. destroy_menu(Menu* this) {
  794.     if (this->head) destroy_item(this->head);
  795.     if (this->menuTitle) destroy_menu(this->menuTitle);
  796.     bzero(this, sizeof *this);
  797.     FREE(this);
  798. }
  799.  
  800. static void
  801. map_menu(Menu* this) {
  802.     Display *dpy = this->popupInfo->dpy;
  803.     if (this->popping) {
  804.     XRaiseWindow(dpy, this->shadowWindow);
  805.     XResizeWindow(dpy, this->shadowWindow, this->width, this->height);
  806.     XResizeWindow(dpy, this->contentWindow, this->width, this->height);
  807.     XRaiseWindow(dpy, this->contentWindow);
  808.     /*
  809.     XMapWindow(dpy, this->shadowWindow); XXXbly
  810.     */
  811.     XMapWindow(dpy, this->contentWindow);
  812.     XMapWindow(dpy, this->shadowWindow);
  813.     this->mapped = True;
  814.     }
  815. }
  816.  
  817. /* XXX try to center menu (title?) under cursor */
  818. /* XXX need a resource for auto-select top item */
  819. /* XXX need a resource for auto-select last item (YEAH) */
  820.  
  821. static void
  822. realize_menu(Menu* this, int rx, int ry) {
  823.     Display *dpy;
  824.     int screenWidth;
  825.     int screenHeight;
  826.     int w, h;
  827.     int left, top;
  828.  
  829.     dpy = this->popupInfo->dpy;
  830.     screenWidth = DisplayWidth(dpy, this->popupInfo->screen);
  831.     screenHeight = DisplayHeight(dpy, this->popupInfo->screen);
  832.  
  833.     if (!this->isTitle) {
  834.     layout_menu(this);    /* Title layout happens from menu layout */
  835.     if (this->overFirst && this->head) {
  836.         rx -= this->head->width/2;
  837.         ry -= this->head->height/2;
  838.     }
  839.     }
  840.     if (rx < 0)
  841.     rx = 0;
  842.     w = this->width + 2;            /* add in border width */
  843.     if (rx + this->width > screenWidth) {
  844.     rx = screenWidth - w;
  845.     }
  846.  
  847.     h = this->height + 2;            /* add in border width */
  848.     if (this->menuTitle) {
  849.     int menuTitleHeight = this->menuTitle->height + Menu_titleYGap;
  850.     if (ry < menuTitleHeight) {
  851.         ry = menuTitleHeight;
  852.     }
  853.     } else if (ry < 0)
  854.     ry = 0;
  855.     if (ry + h > screenHeight) {
  856.     ry = screenHeight - h;
  857.     }
  858.     if (this->menuTitle) {
  859.     realize_menu(this->menuTitle, rx, ry - this->menuTitle->height
  860.                     - Menu_titleYGap);
  861.     }
  862.     XMoveWindow(dpy, this->contentWindow, rx, ry);
  863.     XMoveWindow(dpy, this->shadowWindow, rx + Menu_shadowXOffset,
  864.              ry + Menu_shadowYOffset);
  865.     this->popping = True;
  866.     this->currentItem = 0;
  867.     this->currentSubMenu = 0;
  868.     this->stackPrev = 0;
  869.     map_menu(this);
  870. }
  871.  
  872. static Menu*
  873. find_menu(Menu* this, Window lookfor) {
  874.     MenuItem*    it;
  875.  
  876.     if (lookfor == this->contentWindow)
  877.     return this;
  878.     if (this->menuTitle && this->menuTitle->contentWindow == lookfor)
  879.     return this->menuTitle;
  880.     for (it = this->head; it; it = it->nextItem) {
  881.     Menu* foundBelow;
  882.     if (it->subMenu && (foundBelow = find_menu(it->subMenu, lookfor)))
  883.         return foundBelow;
  884.     }
  885.     return False;
  886. }
  887.  
  888. static int
  889. popup_menu(Menu* this, int b, int rx, int ry) {
  890.     static int alreadyUp = 0;
  891.     PopupInfo* pInfo = this->popupInfo;
  892.     Display    *dpy = pInfo->dpy;
  893.     this->button = b;
  894.  
  895.     if (alreadyUp) {
  896.     fprintf(stderr, "can't call dopup from a menu function\n");
  897.     return -1;
  898.     }
  899.     XInstallColormap(dpy, pInfo->colormap);
  900.     /*XFlush(dpy);XXXbly*/
  901.     this->overFirst = pInfo->overFirst;
  902.     realize_menu(this, rx, ry);
  903.  
  904.     {
  905.     /* should check that the button is still down ... */
  906.     XEvent ev;
  907.     ev.type = ButtonPress;
  908.     grab_everything(&ev, pInfo); /* XXX */
  909.     }
  910.  
  911.     alreadyUp = 1;
  912.     /* Spin, waiting for event that dismisses the menu */
  913.     currentMenu = this;
  914.     set_state(MS_NORMALSTATE);
  915.     for (;;) {
  916.     XEvent ev;
  917.  
  918.     next_event(dpy, &ev);
  919.     switch (ev.type) {
  920.       case KeyPress:
  921.       case KeyRelease:
  922.       case ButtonPress:
  923.         grab_everything(&ev, pInfo);
  924.         break;
  925.       case ButtonRelease:
  926.         grab_everything(&ev, pInfo);
  927.         if (ev.xbutton.button == b) {
  928.         MenuItem* result = currentMenu->currentItem;
  929.         aFuncChain    *chain;
  930.         int        pick = -1;
  931.  
  932.         chain = create_aFuncChain();
  933.         if (result) {
  934.             extend_aFuncChain(chain, result->itemFunc);
  935.             if (result->useMenuFunc) {
  936.             Menu*    amen;
  937.             for (amen = result->parent; amen != NULL; amen = amen->stackPrev)
  938.                 extend_aFuncChain(chain, amen->menuFunc);
  939.             }
  940.         }
  941.         cancel_menu(this);
  942.         if (result && result->enabled && !result->subMenu)
  943.             pick = call_aFuncChain(chain, result->pickValue);
  944.         alreadyUp = 0;
  945.         destroy_aFuncChain(chain);
  946.         return pick;
  947.         }
  948.         break;
  949.       case MotionNotify:
  950.         {
  951.         int haveXY = False;
  952.         int lx, ly;
  953.         Window ww = currentMenu->contentWindow;
  954.         do {
  955.             if (ev.xmotion.window == ww) {
  956.             history[mhead].x = lx = ev.xmotion.x;
  957.             history[mhead].y = ly = ev.xmotion.y;
  958.             mhead = (mhead + 1) % MOTION_HISTORY;
  959.             haveXY = True;
  960.             }
  961.         } while (XCheckMaskEvent(dpy, ButtonMotionMask, &ev));
  962.         if (haveXY)
  963.             move_to(lx, ly);
  964.         }
  965.         break;
  966.       case EnterNotify:
  967.       case LeaveNotify:
  968.         {
  969.         Window window;
  970.         int x, y, type;
  971.  
  972.         do {
  973.             x = ev.xcrossing.x;
  974.             y = ev.xcrossing.y;
  975.             type = ev.type;
  976.             window = ev.xcrossing.window;
  977.         } while (XCheckMaskEvent(dpy,
  978.                 EnterWindowMask|LeaveWindowMask, &ev));
  979.         if (type == EnterNotify)
  980.             enter_menu(window, x, y);
  981.         else if (MenuState != MS_HOGSTATE)
  982.             leave_item_menu(currentMenu);
  983.         }
  984.         break;
  985.       case Expose:
  986.         {
  987.         Window theWindow = ev.xexpose.window;
  988.         Menu* theMenu = find_menu(this, theWindow);
  989.         if (theMenu) {
  990.             int topY = theMenu->height;
  991.             int bottomY = 0;
  992.             do {
  993.             if (ev.xexpose.y < topY) topY = ev.xexpose.y;
  994.             if (ev.xexpose.y+ev.xexpose.height > bottomY)
  995.                 bottomY = ev.xexpose.y+ev.xexpose.height;
  996.             if (ev.xexpose.count == 0)
  997.               render_menu(theMenu, topY, bottomY);
  998.             } while
  999.             (XCheckWindowEvent(dpy, theWindow, ExposureMask, &ev));
  1000.         }
  1001.         break;
  1002.         }
  1003.     }
  1004.     }
  1005. }
  1006.  
  1007. static void
  1008. enter_menu(Window evw, int x, int y) {
  1009.     Menu* m;
  1010.     if ((m = currentMenu->currentSubMenu) && (evw == m->contentWindow)) {
  1011.     /* Entered the current menus sub menu.  Make the sub menu the new */
  1012.     /* current menu. */
  1013.     set_state(MS_NORMALSTATE);
  1014.     m->stackPrev = currentMenu;
  1015.     currentMenu = m;
  1016.     assert(currentMenu->currentItem == 0);
  1017.     assert(currentMenu->currentSubMenu == 0);
  1018.     move_to(x, y);
  1019.     return;
  1020.     }
  1021.  
  1022.     if (MenuState == MS_HOGSTATE
  1023.     && evw != currentMenu->currentSubMenu->contentWindow)
  1024.     return;
  1025.     set_state(MS_NORMALSTATE);
  1026.     if ((m = currentMenu->stackPrev) && (evw == m->contentWindow)) {
  1027.     /* Entered the current menus parent.  Don't cancel the sub menu */
  1028.     /* automatically.  Instead, just revert the currentMenu. */
  1029.     leave_item_menu(currentMenu);
  1030.     currentMenu->stackPrev = 0;
  1031.     currentMenu = m;
  1032.     assert(currentMenu->currentItem != 0);
  1033.     assert(currentMenu->currentSubMenu != 0);
  1034.     move_to(x, y);
  1035.     return;
  1036.     }
  1037.  
  1038.     m = currentMenu;
  1039.     while (m) {
  1040.     if (m->contentWindow == evw) {
  1041.         while (currentMenu != m) {
  1042.         leave_item_menu(currentMenu);
  1043.         currentMenu = currentMenu->stackPrev;
  1044.         }
  1045.         move_to(x, y);
  1046.         return;
  1047.     }
  1048.     m = m->stackPrev;
  1049.     }
  1050.     leave_item_menu(currentMenu);
  1051. }
  1052.  
  1053. static void
  1054. leave_item_menu(Menu* this) {
  1055.     set_state(MS_NORMALSTATE);
  1056.     if (this->currentItem) {
  1057.     leave_item(this->currentItem);
  1058.     this->currentItem = 0;
  1059.     if (this->currentSubMenu) {
  1060.         cancel_menu(this->currentSubMenu);
  1061.         this->currentSubMenu = 0;
  1062.     }
  1063.     }
  1064. }
  1065.  
  1066. /*
  1067. ** Look at the motion history buffer and see if the delta in x is
  1068. ** greater than or equal to the delta in y
  1069. */
  1070.  
  1071. static int
  1072. history_allows(void) {
  1073.     struct hbuf *oldest, *newest;
  1074.     int dx, dy;
  1075.  
  1076.     newest = history + (mhead+MOTION_HISTORY-1)%MOTION_HISTORY;
  1077.     oldest = history + mhead;
  1078.     dx = newest->x - oldest->x;
  1079.     dy = newest->y - oldest->y;
  1080.     return dx > 0 && dy > 0 && dx*2 > dy;
  1081. }
  1082.  
  1083. static void
  1084. move_to(int evx, int evy)
  1085. {
  1086.     MenuItem* it = currentMenu->head;
  1087.     if (MenuState == MS_HOGSTATE)
  1088.     return;
  1089.     while (it) {
  1090.     /* See if evx & evyy are inside an item */
  1091.     if ((evx >= it->x) && (evx < it->x + it->width) &&
  1092.         (evy >= it->y) && (evy < it->y + it->height)) {
  1093.         if (it != currentMenu->currentItem) {
  1094.         if (MenuState == MS_PULLRIGHTACTIVE && history_allows())  {
  1095.             set_state(MS_HOGSTATE);
  1096.             return;
  1097.         }
  1098.         set_state(MS_NORMALSTATE);
  1099.         leave_item_menu(currentMenu);
  1100.         enter_item_menu(currentMenu, it);
  1101.         currentMenu->initialX = evx;
  1102.         }
  1103.         return;
  1104.     }
  1105.     it = it->nextItem;
  1106.     }
  1107.     if (MenuState == MS_PULLRIGHTACTIVE && history_allows())
  1108.     set_state(MS_HOGSTATE);
  1109.     if (MenuState != MS_HOGSTATE)
  1110.     leave_item_menu(currentMenu);
  1111. }
  1112.  
  1113. static void
  1114. enter_item_menu(Menu* this,MenuItem* it) {
  1115.     Display*    dpy = this->popupInfo->dpy;
  1116.     Menu* sub = it->subMenu;
  1117.     this->currentItem = it;
  1118.     enter_item(it);
  1119.  
  1120.     if (it->enabled && sub) {
  1121.     this->currentSubMenu = sub;
  1122.     set_state(MS_PULLRIGHTPENDING);
  1123.     }
  1124. }
  1125.  
  1126. static void
  1127. post_submenu() {
  1128.     Menu* this = currentMenu;
  1129.     MenuItem* it = currentMenu->currentItem;
  1130.     Display*    dpy = currentMenu->popupInfo->dpy;
  1131.     int rootx, rooty;
  1132.     Window child;
  1133.  
  1134.     XTranslateCoordinates(dpy, this->contentWindow,
  1135.       RootWindow(dpy, this->popupInfo->screen),
  1136.       it->x + it->width + Menu_subMenuXOffset,
  1137.       it->y + Menu_subMenuYOffset,
  1138.       &rootx, &rooty, &child);
  1139.     this->currentSubMenu->overFirst = False;
  1140.     realize_menu(this->currentSubMenu, rootx, rooty);
  1141.     XFlush(dpy);
  1142.     set_state(MS_PULLRIGHTACTIVE);
  1143. }
  1144.  
  1145. static void
  1146. cancel_menu(Menu* this) {
  1147.     Display* dpy = this->popupInfo->dpy;
  1148.  
  1149.     XUnmapWindow(dpy, this->shadowWindow);
  1150.     XUnmapWindow(dpy, this->contentWindow);
  1151.     set_state(MS_NORMALSTATE);
  1152.     this->mapped = False;
  1153.     if (this->menuTitle)
  1154.     cancel_menu(this->menuTitle);
  1155.     this->popping = False;
  1156.     leave_item_menu(this);
  1157.     this->stackPrev = 0;
  1158. }
  1159.  
  1160. static void
  1161. render_menu(Menu* this, int startY, int endY) {
  1162.     PopupInfo* pi = this->popupInfo;
  1163.     MenuItem* it = this->head;
  1164.     if (!this->mapped)
  1165.     return;
  1166.     render_outline(pi->dpy, this->contentWindow,
  1167.             this->isTitle ? pi->titleColors : pi->regularColors,
  1168.             0, 0, this->width, this->height);
  1169.     while (it) {
  1170.     if (it->y + it->height >= startY && it->y < endY)
  1171.         render_item(it);
  1172.     it = it->nextItem;
  1173.     }
  1174. }
  1175.  
  1176. static void
  1177. append_item_menu(Menu* this, MenuItem* it) {
  1178.     if (this->head)
  1179.     this->tail->nextItem = it;
  1180.     else
  1181.     this->head = it;
  1182.     this->tail = it;
  1183. }
  1184.  
  1185. static void
  1186. layout_menu(Menu* this) {
  1187.     /* Find maximum width and total height.  Position each menu-item at */
  1188.     /* its final x & y relative to the menu window. */
  1189.     short maxWidth = 0;
  1190.     int y = Menu_topInset;
  1191.     int checkAdjust = 0;
  1192.     MenuItem* it = this->head;
  1193.     int arrowAdjust = 0;
  1194.     if (this->layoutDone)
  1195.     return;
  1196.     this->layoutDone = 1;
  1197.     while (it) {
  1198.     short iw, ih;
  1199.     MenuItem* next = it->nextItem;
  1200.     reset_MenuItem(it);
  1201.     iw = it->width;
  1202.     ih = it->height;
  1203.     if (it->subMenu) {
  1204.         arrowAdjust = Menu_arrowAdjust;
  1205.         if (ih < Menu_arrowHeight) {
  1206.         ih = Menu_arrowHeight;
  1207.         resize_item(it, iw, ih);
  1208.         }
  1209.     }
  1210.     if (it->type == MenuItem_hasCheckBox)
  1211.         checkAdjust = Menu_checkAdjust;
  1212.     if (it->label && ih < Menu_checkHeight) ih = Menu_checkHeight;
  1213.     if (iw > maxWidth) maxWidth = iw;
  1214.     move_item(it, Menu_leftInset, y);
  1215.     y += ih;
  1216.     if (it->label && next)
  1217.         y += Menu_itemOffset;
  1218.     it = next;
  1219.     }
  1220.     maxWidth +=
  1221.     checkAdjust + arrowAdjust + Menu_itemLeftInset + Menu_itemRightInset;
  1222.     this->width = maxWidth + Menu_widthAdjust;
  1223.     if (this->menuTitle) {
  1224.     int tw;
  1225.     layout_menu(this->menuTitle);
  1226.     tw = this->menuTitle->width;
  1227.     if (this->width <= tw) {
  1228.         int adjust = tw - this->width;
  1229.         this->width = tw;
  1230.         maxWidth += adjust;
  1231.     } else
  1232.         resize_menu(this->menuTitle,this->width,this->menuTitle->height);
  1233.     }
  1234.     this->height = y + Menu_bottomInset;
  1235.  
  1236.     /* Now fix the widths of the menu items */
  1237.     for (it = this->head; (it) ; it = it->nextItem) {
  1238.     it->inset = checkAdjust;
  1239.     resize_item(it, maxWidth, it->height);
  1240.     }
  1241. }
  1242.  
  1243. static void
  1244. resize_menu(Menu* this, int w, int h) {
  1245.     this->width = w;
  1246.     this->height = h;
  1247. }
  1248.  
  1249. static MenuItem*
  1250. find_item(Menu* this, int itemNo) {
  1251.     MenuItem* it;
  1252.  
  1253.     for (it = this->head; it; it = it->nextItem)
  1254.     if (it->index == itemNo)
  1255.         return it;
  1256.     return 0;
  1257. }
  1258.  
  1259. /*----------------------------------------------------------------------*/
  1260.  
  1261. static void
  1262. draw_arrow (Display* dpy, Window win, GC in, GC edge, int x, int y, int w, int h) {
  1263.     XPoint p[5];
  1264.     p[0].x = p[4].x = x;
  1265.     p[0].y = p[4].y = y;
  1266.     p[1].x = x + w - 1;
  1267.     p[1].y = y + (h / 2) - 1;
  1268.     p[2].x = p[1].x;
  1269.     p[2].y = p[1].y + 1;
  1270.     p[3].x = x;
  1271.     p[3].y = y + h - 1;
  1272.     XFillPolygon(dpy, win, in, p, 4, Convex, CoordModeOrigin);
  1273.     XDrawLines(dpy, win, edge, p, 5, CoordModeOrigin);
  1274. }
  1275.  
  1276. /*----------------------------------------------------------------------*/
  1277.  
  1278. static MenuItem*
  1279. create_item(Menu* m, int ix, char* label) {
  1280.     MenuItem* this = ALLOC(sizeof *this);
  1281.     this->parent = m;
  1282.     append_item_menu(m, this);
  1283.     this->nextItem = 0;
  1284.     this->type = MenuItem_normal;
  1285.     this->enabled = True;
  1286.     this->visualState = MenuItem_quiet;
  1287.     this->checkState = 0;
  1288.     this->inset = 0;
  1289.     this->subMenu = 0;
  1290.     this->index = this->pickValue = ix;
  1291.     this->useMenuFunc = True;
  1292.     if (label) {
  1293.     this->label = STRDUP(label);
  1294.     this->len = strlen(label);
  1295.     this->trueWidth = this->width =
  1296.         strwidth_font(m->menuFont, label, this->len);
  1297.     this->trueHeight = this->height = Menu_itemTopInset
  1298.         + m->menuFont->finfo->ascent + m->menuFont->finfo->descent
  1299.         + Menu_itemBottomInset;
  1300.     } else {
  1301.     this->label = 0;
  1302.     this->trueWidth = this->width = 0;
  1303.     this->trueHeight = this->height = 3;
  1304.     this->enabled = False;
  1305.     }
  1306.     return this;
  1307. }
  1308.  
  1309. static void
  1310. destroy_item(MenuItem* this) {
  1311.     if (this->nextItem) destroy_item(this->nextItem);
  1312.     if (this->label) FREE(this->label);
  1313.     bzero(this, sizeof *this);
  1314.     FREE(this);
  1315. }
  1316.  
  1317. static void
  1318. set_enable_item(MenuItem* this, int on) {
  1319.     this->visualState = on ? MenuItem_quiet : MenuItem_disabled;
  1320.     this->enabled = on;
  1321. }
  1322.  
  1323. static void
  1324. set_check_item(MenuItem* this, int on) {
  1325.     this->type = (on ? MenuItem_hasCheckBox : MenuItem_normal);
  1326.     this->checkState = on;
  1327.     this->parent->layoutDone = False;
  1328. }
  1329.  
  1330. static void
  1331. render_background_item(MenuItem* this) {
  1332.     PopupInfo *pi = this->parent->popupInfo;
  1333.     Display* dpy = pi->dpy;
  1334.     Window win = this->parent->contentWindow;
  1335.  
  1336.     if (this->visualState == MenuItem_selected) {
  1337.     render_fill(dpy, win, pi->selectedColors, pi->white,
  1338.             this->x, this->y, this->width, this->height);
  1339.     }
  1340.     if (this->subMenu) {
  1341.     int xx = this->x + this->width
  1342.         -Menu_itemRightInset-Menu_arrowRightInset-Menu_arrowWidth;
  1343.     int yy = this->y + (this->height - Menu_arrowHeight) / 2;
  1344.     GC fg, bg;
  1345.     if (this->visualState == MenuItem_disabled)
  1346.         fg = pi->lightWhiteGrey, bg = pi->darkGrey;
  1347.     else
  1348.         fg = pi->white, bg = pi->black;
  1349.     draw_arrow(dpy, win, fg, bg, xx, yy,
  1350.              Menu_arrowWidth, Menu_arrowHeight);
  1351.     XDrawLine(dpy, win, bg, xx, yy + Menu_arrowHeight,
  1352.                xx + Menu_arrowWidth - 1, yy + Menu_arrowHeight/2+1);
  1353.     }
  1354.     if (this->type == MenuItem_hasCheckBox)
  1355.     draw_check(pi, win, this->checkState, this->x, this->y, this->height);
  1356. }
  1357.  
  1358. static void
  1359. enter_item(MenuItem *this) {
  1360.     if (this->enabled) {
  1361.     this->visualState = MenuItem_selected;
  1362.     render_item(this);
  1363.     }
  1364. }
  1365.  
  1366. static void
  1367. leave_item(MenuItem *this) {
  1368.     if (this->enabled) {
  1369.     this->visualState = MenuItem_quiet;
  1370.     XClearArea(this->parent->popupInfo->dpy, this->parent->contentWindow,
  1371.             this->x, this->y,
  1372.             this->width, this->height, False);
  1373.     if (this->parent->mapped)
  1374.         render_item(this);
  1375.     }
  1376. }
  1377.  
  1378. static void
  1379. render_item(MenuItem *this) {
  1380.     PopupInfo *pi = this->parent->popupInfo;
  1381.     Window    w = this->parent->contentWindow;
  1382.     if (this->label) {
  1383.     int xx = this->x + Menu_itemLeftInset;
  1384.     int yy = this->y + Menu_itemTopInset;
  1385.     GC gc;
  1386.  
  1387.     render_background_item(this);
  1388.     if (this->visualState == MenuItem_disabled)
  1389.         gc = pi->darkGrey;
  1390.     else
  1391.         gc = pi->black;
  1392.     XSetFont(pi->dpy, gc, this->parent->menuFont->finfo->fid);
  1393.     XDrawString(pi->dpy, w, gc,
  1394.             xx+this->inset, yy + this->parent->menuFont->finfo->ascent,
  1395.             this->label, this->len);
  1396.     } else {
  1397.     int yy = this->y;
  1398.     int xr = this->x + this->width;
  1399.     XDrawLine(pi->dpy, w, pi->darkGrey, this->x, yy, xr, yy);
  1400.     yy++;
  1401.     XDrawLine(pi->dpy, w, pi->white, this->x, yy, xr, yy);
  1402.     }
  1403. }
  1404.  
  1405. static void
  1406. resize_item(MenuItem* this, int w, int h) {
  1407.     this->width = w;
  1408.     this->height = h;
  1409. }
  1410.  
  1411. static void
  1412. reset_MenuItem(MenuItem* this) {
  1413.     this->width = this->trueWidth;
  1414.     this->height = this->trueHeight;
  1415. }
  1416.  
  1417. static void
  1418. move_item(MenuItem* this, int x, int y) {
  1419.     this->x = x;
  1420.     this->y = y;
  1421. }
  1422.  
  1423. static void
  1424. render_edges (Display* dpy, Window win, GC *colors, int x, int y, int w, int h) {
  1425.     XPoint    p[3];
  1426.  
  1427.     p[0].x = x;            /* Start in lower left */
  1428.     p[0].y = y + h - 1;
  1429.     p[1].x = x;            /* Go to top left */
  1430.     p[1].y = y;
  1431.     p[2].x = x + w - 2;        /* Finish in upper right */
  1432.     p[2].y = y;
  1433.     XDrawLines(dpy, win, colors[0], p, 3, CoordModeOrigin);
  1434.  
  1435.     p[0].x = x + 1;        /* Start in lower left */
  1436.     p[0].y = y + h - 1;
  1437.     p[1].x = x + w - 1;        /* Go to lower right */
  1438.     p[1].y = y + h - 1;
  1439.     p[2].x = x + w - 1;        /* Finish in upper right */
  1440.     p[2].y = y;
  1441.     XDrawLines(dpy, win, colors[1], p, 3, CoordModeOrigin);
  1442. }
  1443.  
  1444. static void
  1445. render_outline (Display* dpy, Window win, GC *colors, int x, int y, int w, int h) {
  1446.     render_edges(dpy, win, colors, x, y, w, h);
  1447.     render_edges(dpy, win, colors+2, x+1, y+1, w-2, h-2);
  1448. }
  1449.  
  1450. static void
  1451. render_fill(Display* dpy, Window win, GC *colors, GC fillColor,
  1452.     int x, int y, int w, int h) {
  1453.     render_outline(dpy, win, colors, x, y, w, h);
  1454.     XFillRectangle(dpy, win, fillColor, x+2, y+2, w-4, h-4);
  1455. }
  1456.  
  1457. static void
  1458. draw_check(PopupInfo* pi, Window w, int on, int x, int y, int ih) {
  1459.     GC *gcs;
  1460.  
  1461.     gcs = (on ? pi->checkBoxOnColors : pi->checkBoxOffColors);
  1462.     x += Menu_checkLeftInset;
  1463.     y += (ih - Menu_checkHeight) / 2;
  1464.     if (on)
  1465.     render_fill(pi->dpy, w, gcs, pi->lightGrey,
  1466.         x, y, Menu_checkWidth, Menu_checkHeight);
  1467.     if (on & PUP_CHECK) {
  1468.     int xo = (x + 2) % checkWidth;
  1469.     int yo = (y + 2) % checkHeight;
  1470.     XSetFillStyle(pi->dpy, pi->black, FillStippled);
  1471.     XSetTSOrigin(pi->dpy, pi->black, xo, yo);
  1472.     XFillRectangle(pi->dpy, w, pi->black,
  1473.                 x + 2, y + 2, checkWidth, checkHeight);
  1474.     XSetFillStyle(pi->dpy, pi->black, FillSolid);
  1475.     }
  1476. }
  1477.  
  1478. static void
  1479. set_state(int newState) {
  1480.     int delay;
  1481.     struct tms t;
  1482.  
  1483.     switch (MenuState = newState) {
  1484.     case MS_PULLRIGHTACTIVE:
  1485.     case MS_NORMALSTATE: StateTimeout = 0; return;
  1486.  
  1487.     case MS_HOGSTATE: delay = HogTimeout; break;
  1488.     case MS_PULLRIGHTPENDING: delay = PostTimeout; break;
  1489.     }
  1490.     delay = (delay * HZ) / 1000;
  1491.     StateTimeout = times(&t) + delay;
  1492. }
  1493.  
  1494. static void
  1495. timeout_happened(void) {
  1496.     switch (MenuState) {
  1497.     case MS_PULLRIGHTPENDING:
  1498.         post_submenu();
  1499.         break;
  1500.     case MS_HOGSTATE: {
  1501.         struct hbuf *last;
  1502.  
  1503.         cancel_menu(currentMenu->currentSubMenu);
  1504.         currentMenu->currentSubMenu = 0;
  1505.         set_state(MS_NORMALSTATE);
  1506.         last = history + (mhead+MOTION_HISTORY-1)%MOTION_HISTORY;
  1507.         move_to(last->x, last->y);
  1508.         break;
  1509.     }
  1510.     default:
  1511.         set_state(MS_NORMALSTATE);
  1512.         break;
  1513.     }
  1514. }
  1515.  
  1516. static void
  1517. next_event(Display* dpy, XEvent* pEv) {
  1518.     struct tms tms;
  1519.     if (StateTimeout) {
  1520.     unsigned int now = times(&tms);
  1521.  
  1522.     if (now > StateTimeout)
  1523.         timeout_happened();
  1524.     if (StateTimeout) {
  1525.         int    fd;
  1526.  
  1527.         if (XEventsQueued(dpy, QueuedAfterFlush) == 0) {
  1528.         fd = ConnectionNumber(dpy);
  1529.         do {
  1530.             struct timeval t;
  1531.             fd_set fds;
  1532.             unsigned int delta;
  1533.  
  1534.             FD_ZERO(&fds);
  1535.             FD_SET(fd, &fds);
  1536.             delta = StateTimeout - now;
  1537.             t.tv_sec = delta / HZ;
  1538.             t.tv_usec = (delta % HZ) * 1000000 / HZ;
  1539.             switch (select(fd+1, &fds, 0, 0, &t)) {
  1540.             case 0:        /* timeout happened */
  1541.                 timeout_happened();
  1542.                 if (StateTimeout == 0) {
  1543.                 fd = -1;
  1544.                 break;
  1545.                 }
  1546.                 /* else do it again, fall through to .. */
  1547.             case -1:    /* Error, should call select again */
  1548.                 now = times(&tms);
  1549.                 break;
  1550.             default:    /* Data is ready */
  1551.                 if (XEventsQueued(dpy, QueuedAfterReading) == 0)
  1552.                 fprintf(stderr, "botch\n"), abort();
  1553.                 fd = -1;
  1554.                 break;
  1555.             }
  1556.         } while (fd >= 0);
  1557.         }
  1558.     }
  1559.     }
  1560.     XNextEvent(dpy, pEv);
  1561. }
  1562.